﻿namespace YidanSoft.Library.EditorUtility.WinControls
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Drawing;
    using System.Reflection;
    using System.Resources;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using YidanSoft.Library.EditorUtility.General;
    using YidanSoft.Library.EditorUtility.Win32;

    [ToolboxItem(false)]
    public class ListViewEx : ListView
    {
        private const int ARROW_WIDTH = 12;
        private const int BUFFER_SIZE = 0x400;
        private ImageList checkBoxesImageList;
        private int counter = 0;
        private HeaderHook headerHook = null;
        private ArrayList headerIconsList = new ArrayList();
        private ImageList headerImageList;
        private IntPtr hHeader = IntPtr.Zero;
        private const int IMAGE_WIDTH = 0x10;
        private Point lastMousePosition;
        private int lastSortedColumn = 0;
        private int pressedHeaderItem = -1;
        private ArrayList rowSorterList = new ArrayList();
        private bool setInitialSortColumn = false;
        private const int TEXT_TO_ARROW_GAP = 15;
        private bool tracking = false;

        public ListViewEx()
        {
            base.SetStyle(ControlStyles.UserPaint, false);
            base.FullRowSelect = true;
            base.View = View.Details;
            base.HeaderStyle = ColumnHeaderStyle.Nonclickable;
            this.InitializeCheckBoxesImageList();
            base.ListViewItemSorter = new CompareListItems(this);
        }

        private Rectangle AdjustFirstItemRect(Rectangle rc)
        {
            ListViewItem item = base.Items[0];
            if (base.Columns.Count > 1)
            {
                Debug.WriteLine(rc);
                Rectangle subItemRect = this.GetSubItemRect(0, 1);
                return new Rectangle(rc.Left, rc.Top, subItemRect.Left - rc.Left, rc.Height);
            }
            return rc;
        }

        private void DoHeaderCustomDrawing(ref Message m)
        {
            int num3;
            YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW lParam = (YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW) m.GetLParam(typeof(YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW));
            Graphics graphics = Graphics.FromHdc(lParam.hdc);
            Rectangle headerCtrlRect = this.GetHeaderCtrlRect();
            headerCtrlRect = this.GetHeaderItemRect(lParam.dwItemSpec);
            int num = headerCtrlRect.Left + headerCtrlRect.Width;
            graphics.FillRectangle(new SolidBrush(SystemColors.ScrollBar), headerCtrlRect.Left, headerCtrlRect.Top, headerCtrlRect.Width, headerCtrlRect.Height);
            if (!(((lParam.dwItemSpec != this.PressedHeaderItem) || this.IsCursorOnDivider()) || this.Tracking))
            {
                this.PressedHeaderItem = -1;
                headerCtrlRect.Inflate(-1, -1);
                graphics.FillRectangle(new SolidBrush(ColorUtil.VSNetPressedColor), headerCtrlRect.Left, headerCtrlRect.Top, headerCtrlRect.Width, headerCtrlRect.Height);
            }
            else
            {
                ControlPaint.DrawBorder3D(graphics, headerCtrlRect.Left, headerCtrlRect.Top, headerCtrlRect.Width, headerCtrlRect.Height - 1, Border3DStyle.RaisedInner, Border3DSide.All);
            }
            string headerItemText = this.GetHeaderItemText(lParam.dwItemSpec);
            Debug.WriteLine(headerItemText);
            Size size = TextUtil.GetTextSize(graphics, headerItemText, this.Font);
            int num2 = 4;
            Point point = new Point(headerCtrlRect.Left + num2, headerCtrlRect.Top + ((headerCtrlRect.Height - size.Height) / 2));
            if (((this.headerIconsList != null) && this.HasHeaderImage(lParam.dwItemSpec, out num3)) && (num3 != -1))
            {
                Image image = this.headerImageList.Images[num3];
                if (image != null)
                {
                    graphics.DrawImage(this.headerImageList.Images[num3], headerCtrlRect.Left + num2, headerCtrlRect.Top);
                    point.X += 0x10;
                    headerCtrlRect.Width -= 0x10;
                }
            }
            if (lParam.dwItemSpec == this.lastSortedColumn)
            {
                int x = point.X + 2;
                x += size.Width + 15;
                Rectangle rc = new Rectangle(x, headerCtrlRect.Top, 12, headerCtrlRect.Height);
                if (num >= ((x + 12) + 4))
                {
                    if ((base.Sorting == SortOrder.Ascending) || (base.Sorting == SortOrder.None))
                    {
                        this.DrawUpArrow(graphics, rc);
                    }
                    else
                    {
                        this.DrawDownArrow(graphics, rc);
                    }
                }
            }
            int num5 = 8;
            Rectangle rect = new Rectangle(point.X + 2, point.Y, (headerCtrlRect.Width - num2) - num5, headerCtrlRect.Height);
            TextUtil.DrawText(graphics, headerItemText, this.Font, rect);
            graphics.Dispose();
            m.Result = (IntPtr) 4L;
        }

        private void DoListCustomDrawing(ref Message m)
        {
            YidanSoft.Library.EditorUtility.Win32.NMLVCUSTOMDRAW lParam = (YidanSoft.Library.EditorUtility.Win32.NMLVCUSTOMDRAW) m.GetLParam(typeof(YidanSoft.Library.EditorUtility.Win32.NMLVCUSTOMDRAW));
            int dwItemSpec = lParam.nmcd.dwItemSpec;
            int iSubItem = lParam.iSubItem;
            Debug.Assert(base.Items.Count != 0);
            ListViewItem item = base.Items[dwItemSpec];
            Rectangle subItemRect = this.GetSubItemRect(dwItemSpec, iSubItem);
            Graphics graphics = Graphics.FromHdc(lParam.nmcd.hdc);
            if (this.IsRowSelected(dwItemSpec))
            {
                int num3 = 2;
                if (base.GridLines)
                {
                    num3 = 3;
                }
                graphics.FillRectangle(new SolidBrush(ColorUtil.VSNetSelectionColor), (int) (subItemRect.Left + 1), (int) (subItemRect.Top + 1), (int) (subItemRect.Width - 2), (int) (subItemRect.Height - num3));
                if (iSubItem == 0)
                {
                    Color highlight = SystemColors.Highlight;
                    int num4 = 1;
                    if (base.GridLines)
                    {
                        num4 = 2;
                    }
                    graphics.DrawRectangle(new Pen(highlight), subItemRect.Left + 1, subItemRect.Top, subItemRect.Width - 2, subItemRect.Height - num4);
                }
            }
            else if (iSubItem == this.lastSortedColumn)
            {
                if (iSubItem == 0)
                {
                    subItemRect = this.AdjustFirstItemRect(subItemRect);
                }
                graphics.FillRectangle(new SolidBrush(Color.FromArgb(0xf7, 0xf7, 0xf7)), subItemRect.Left, subItemRect.Top, subItemRect.Width, subItemRect.Height);
            }
            else
            {
                if (iSubItem == 0)
                {
                    subItemRect = this.AdjustFirstItemRect(subItemRect);
                }
                graphics.FillRectangle(new SolidBrush(SystemColors.Window), subItemRect.Left, subItemRect.Top, subItemRect.Width, subItemRect.Height);
            }
            if (iSubItem == 0)
            {
                subItemRect = this.AdjustFirstItemRect(subItemRect);
            }
            string subItemText = this.GetSubItemText(dwItemSpec, iSubItem);
            Size size = TextUtil.GetTextSize(graphics, subItemText, this.Font);
            int num5 = 4;
            Point point = new Point(subItemRect.Left + num5, subItemRect.Top + ((subItemRect.Height - size.Height) / 2));
            int num6 = 8;
            if (base.CheckBoxes && (iSubItem == 0))
            {
                int num7 = 0;
                if (item.Checked)
                {
                    num7 = 1;
                }
                graphics.DrawImage(this.checkBoxesImageList.Images[num7], subItemRect.Left + num5, subItemRect.Top);
                point.X += 0x10;
                subItemRect.Width -= 0x10;
            }
            else if ((((iSubItem == 0) && !base.CheckBoxes) && (item.ImageIndex != -1)) && (item.ImageList != null))
            {
                ImageList imageList = item.ImageList;
                Image image = imageList.Images[item.ImageIndex];
                if (image != null)
                {
                    graphics.DrawImage(imageList.Images[item.ImageIndex], subItemRect.Left + num5, subItemRect.Top);
                    point.X += 0x10;
                    subItemRect.Width -= 0x10;
                }
            }
            Rectangle rect = new Rectangle(point.X + 2, point.Y, (subItemRect.Width - num5) - num6, subItemRect.Height);
            TextUtil.DrawText(graphics, subItemText, this.Font, rect);
            graphics.Dispose();
            Marshal.StructureToPtr(lParam, m.LParam, true);
            m.Result = (IntPtr) 4L;
        }

        private void DrawDownArrow(Graphics g, Rectangle rc)
        {
            int num = rc.Left + (rc.Width / 2);
            int num2 = num - 6;
            int num3 = (rc.Height - 6) / 2;
            int num4 = num + 6;
            int num5 = (rc.Height - 6) / 2;
            int num6 = num5 + 6;
            g.DrawLine(new Pen(SystemColors.ControlDarkDark), num2, num3, num, num6);
            g.DrawLine(new Pen(Color.White), num4, num5, num, num6);
            g.DrawLine(new Pen(SystemColors.ControlDarkDark), num2, num3, num4, num5);
        }

        private void DrawUpArrow(Graphics g, Rectangle rc)
        {
            int num = rc.Left + (rc.Width / 2);
            int num2 = (rc.Height - 6) / 2;
            int num3 = num - 6;
            int num4 = num2 + 6;
            int num5 = num + 6;
            int num6 = num2 + 6;
            g.DrawLine(new Pen(SystemColors.ControlDarkDark), num3, num4, num, num2);
            g.DrawLine(new Pen(Color.White), num5, num6, num, num2);
            g.DrawLine(new Pen(Color.White), num3, num4, num5, num6);
        }

        private Rectangle GetHeaderCtrlRect()
        {
            YidanSoft.Library.EditorUtility.Win32.RECT rc = new YidanSoft.Library.EditorUtility.Win32.RECT();
            WindowsAPI.GetClientRect(this.hHeader, ref rc);
            return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
        }

        public Rectangle GetHeaderItemRect(int index)
        {
            YidanSoft.Library.EditorUtility.Win32.RECT lParam = new YidanSoft.Library.EditorUtility.Win32.RECT();
            WindowsAPI.SendMessage(this.hHeader, 0x1207, index, ref lParam);
            return new Rectangle(lParam.left, lParam.top, lParam.right - lParam.left, lParam.bottom - lParam.top);
        }

        protected string GetHeaderItemText(int index)
        {
            HDITEM lParam = new HDITEM();
            lParam.mask = 2;
            lParam.cchTextMax = 0x400;
            lParam.pszText = Marshal.AllocHGlobal(0x400);
            WindowsAPI.SendMessage(this.hHeader, 0x120b, index, ref lParam);
            return Marshal.PtrToStringAuto(lParam.pszText);
        }

        internal RowSorterHelper GetRowSorterHelper()
        {
            for (int i = 0; i < this.rowSorterList.Count; i++)
            {
                RowSorterHelper helper = (RowSorterHelper) this.rowSorterList[i];
                if ((helper != null) && (helper.ColumnIndex == this.LastSortedColumn))
                {
                    return helper;
                }
            }
            return null;
        }

        private Rectangle GetSubItemRect(int row, int col)
        {
            YidanSoft.Library.EditorUtility.Win32.RECT lParam = new YidanSoft.Library.EditorUtility.Win32.RECT();
            lParam.top = col;
            lParam.left = 0;
            WindowsAPI.SendMessage(base.Handle, 0x1038, row, ref lParam);
            return new Rectangle(lParam.left, lParam.top, lParam.right - lParam.left, lParam.bottom - lParam.top);
        }

        private string GetSubItemText(int row, int col)
        {
            YidanSoft.Library.EditorUtility.Win32.LVITEM lParam = new YidanSoft.Library.EditorUtility.Win32.LVITEM();
            lParam.iItem = row;
            lParam.mask = 1;
            lParam.iSubItem = col;
            lParam.cchTextMax = 0x400;
            lParam.pszText = Marshal.AllocHGlobal(0x400);
            WindowsAPI.SendMessage(base.Handle, 0x1073, row, ref lParam);
            return Marshal.PtrToStringAuto(lParam.pszText);
        }

        private bool HasHeaderImage(int headerIndex, out int imageIndex)
        {
            imageIndex = -1;
            for (int i = 0; i < this.headerIconsList.Count; i++)
            {
                HeaderIconHelper helper = (HeaderIconHelper) this.headerIconsList[i];
                if ((helper != null) && (helper.HeaderIndex == headerIndex))
                {
                    imageIndex = helper.IconIndex;
                    return true;
                }
            }
            return false;
        }

        private void InitializeCheckBoxesImageList()
        {
            this.checkBoxesImageList = new ImageList();
            this.checkBoxesImageList.ImageSize = new Size(0x10, 0x10);
            Assembly assembly = Assembly.GetAssembly(System.Type.GetType("YidanSoft.Library.EditorUtility.WinControls.ListViewEx"));
            ResourceManager manager = new ResourceManager("YidanSoft.Library.EditorUtility.Resources.SortedListView", assembly);
            Bitmap bitmap = (Bitmap) manager.GetObject("CheckBox");
            bitmap.MakeTransparent(Color.FromArgb(0, 0x80, 0x80));
            this.checkBoxesImageList.Images.AddStrip(bitmap);
        }

        protected bool IsCursorOnDivider()
        {
            HD_HITTESTINFO hti = new HD_HITTESTINFO();
            hti.pt.x = this.LastMousePosition.X;
            hti.pt.y = this.LastMousePosition.Y;
            WindowsAPI.SendMessage(this.hHeader, 0x1206, 0, ref hti);
            return (hti.flags == 4);
        }

        private bool IsRowSelected(int row)
        {
            Debug.Assert((row >= 0) && (row < base.Items.Count));
            ListViewItem item = base.Items[row];
            return item.Selected;
        }

        private bool NotifyHeaderCustomDraw(ref Message m)
        {
            m.Result = (IntPtr) 0L;
            YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW lParam = (YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW) m.GetLParam(typeof(YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW));
            if (lParam.hdr.hwndFrom == this.hHeader)
            {
                switch (lParam.dwDrawStage)
                {
                    case 1:
                        m.Result = (IntPtr) 0x20L;
                        break;

                    case 0x10001:
                        this.DoHeaderCustomDrawing(ref m);
                        break;
                }
            }
            return false;
        }

        private bool NotifyListCustomDraw(ref Message m)
        {
            m.Result = (IntPtr) 0L;
            YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW lParam = (YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW) m.GetLParam(typeof(YidanSoft.Library.EditorUtility.Win32.NMCUSTOMDRAW));
            IntPtr handle = base.Handle;
            if (lParam.hdr.hwndFrom == base.Handle)
            {
                switch (lParam.dwDrawStage)
                {
                    case 1:
                        m.Result = (IntPtr) 0x20L;
                        break;

                    case 0x10001:
                        m.Result = (IntPtr) 0x20L;
                        break;

                    case 0x30001:
                        this.DoListCustomDrawing(ref m);
                        break;
                }
            }
            return false;
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            this.hHeader = WindowsAPI.GetDlgItem(base.Handle, 0);
            Debug.Assert(this.hHeader != IntPtr.Zero, "Fail to get Header Control Windows Handle...");
            this.headerHook = new HeaderHook(this);
            this.headerHook.AssignHandle(this.hHeader);
        }

        private void PaintBackground(IntPtr hDC)
        {
            Graphics graphics = Graphics.FromHdc(hDC);
            if (this.lastSortedColumn != -1)
            {
                Rectangle subItemRect = this.GetSubItemRect(0, this.lastSortedColumn);
                if ((this.lastSortedColumn == 0) && (base.Columns.Count > 1))
                {
                    Rectangle rectangle2 = this.GetSubItemRect(0, 1);
                    subItemRect = new Rectangle(subItemRect.Left, subItemRect.Top, rectangle2.Left - subItemRect.Left, subItemRect.Height);
                }
                graphics.FillRectangle(new SolidBrush(Color.FromArgb(0xf7, 0xf7, 0xf7)), subItemRect.Left, subItemRect.Top, subItemRect.Width, base.Height);
            }
        }

        public void SetColumnSortFormat(int columnIndex, SortedListViewFormatType format)
        {
            this.rowSorterList.Add(new RowSorterHelper(columnIndex, format));
        }

        public void SetColumnSortFormat(int columnIndex, SortedListViewFormatType format, ListSortEvent callBack)
        {
            this.rowSorterList.Add(new RowSorterHelper(columnIndex, format, callBack));
        }

        public void SetHeaderIcon(int headerIndex, int iconIndex)
        {
            this.headerIconsList.Add(new HeaderIconHelper(headerIndex, iconIndex));
        }

        protected override void WndProc(ref Message message)
        {
            base.WndProc(ref message);
            int msg = message.Msg;
            switch (msg)
            {
                case 20:
                {
                    IntPtr wParam = message.WParam;
                    this.PaintBackground(wParam);
                    break;
                }
                case 0x4e:
                {
                    YidanSoft.Library.EditorUtility.Win32.NMHDR lParam = (YidanSoft.Library.EditorUtility.Win32.NMHDR) message.GetLParam(typeof(YidanSoft.Library.EditorUtility.Win32.NMHDR));
                    msg = lParam.code;
                    switch (msg)
                    {
                        case -327:
                            this.Tracking = false;
                            break;

                        case -326:
                            this.Tracking = true;
                            break;
                    }
                    if (msg == -12)
                    {
                        Debug.Write("CUSTOM DRAWING");
                        Debug.WriteLine(this.counter++);
                        this.NotifyHeaderCustomDraw(ref message);
                    }
                    break;
                }
                case 0x204e:
                {
                    YidanSoft.Library.EditorUtility.Win32.NMHDR nmhdr2 = (YidanSoft.Library.EditorUtility.Win32.NMHDR) message.GetLParam(typeof(YidanSoft.Library.EditorUtility.Win32.NMHDR));
                    switch (nmhdr2.code)
                    {
                        case -177:
                            Debug.WriteLine("List View GetDispInfo Notifications");
                            break;

                        case -12:
                            this.NotifyListCustomDraw(ref message);
                            break;
                    }
                    break;
                }
            }
        }

        public ImageList HeaderImageList
        {
            get
            {
                return this.headerImageList;
            }
            set
            {
                this.headerImageList = value;
            }
        }

        public int InitialSortedColumn
        {
            set
            {
                if (!this.setInitialSortColumn)
                {
                    this.setInitialSortColumn = true;
                    this.lastSortedColumn = value;
                }
            }
        }

        internal Point LastMousePosition
        {
            get
            {
                return this.lastMousePosition;
            }
            set
            {
                this.lastMousePosition = value;
            }
        }

        internal int LastSortedColumn
        {
            get
            {
                return this.lastSortedColumn;
            }
            set
            {
                this.lastSortedColumn = value;
                base.Invalidate();
            }
        }

        internal int PressedHeaderItem
        {
            get
            {
                return this.pressedHeaderItem;
            }
            set
            {
                this.pressedHeaderItem = value;
            }
        }

        internal bool Tracking
        {
            get
            {
                return this.tracking;
            }
            set
            {
                this.tracking = value;
            }
        }
    }
}

